Skip to content

refactor(security): improve SecureRandom implementation, tests, and docs#56224

Open
joshtrichards wants to merge 7 commits into
masterfrom
jtr/refactor-SecureRandom
Open

refactor(security): improve SecureRandom implementation, tests, and docs#56224
joshtrichards wants to merge 7 commits into
masterfrom
jtr/refactor-SecureRandom

Conversation

@joshtrichards
Copy link
Copy Markdown
Member

Summary

This PR improves the SecureRandom implementation along with its related tests and documentation.

Key changes

  • Refactored implementation:

    • Added strict checks to prevent accidental use of non-ASCII, duplicate-containing, or overly short character sets.
      • This is a security-hardening change: it prevents developers from making subtle, security-impacting mistakes.
      • Previously, passing a character set shorter than 4 characters, containing multibyte characters, or with duplicates would (most likely) have resulted in unexpected, but not necessarily fatal, behavior. The function would still "work," but might return poor quality random strings, cause subtle bugs, or perhaps fail in different ways.
      • Technically a backward-incompatible (breaking) change, but unlikely to be disruptive in practice:
        • No known use cases intentionally violate these checks (any that do are likely unintentional, so this will surface them while preventing future occurrences).
    • Established a new constant containing the (existing) default $characters set passed to generate().
    • Improved docblocks and in-code documentation for ISecureRandom and SecureRandom to better describe behavior, parameters, and usage.
  • Expanded and updated tests:

    • Added new tests for:
      • Invalid character sets (duplicates, too short, non-ASCII)
      • Unusual/oddball lengths (e.g., 31) to check for off-by-one and fencepost errors
      • Minimum viable character set size and large custom sets
      • Output uniqueness (basic sanity check)
      • Default and user-supplied character sets
    • Improved existing test coverage and runtime performance:
      • Replaced impractically large test lengths (e.g., 64,000) with more practical ranges for CI, while still ensuring coverage of edge cases and real-world needs.

TODO

Checklist

@joshtrichards joshtrichards added enhancement tests Related to tests ♻️ refactor Refactor code (not a bug fix, not a feature just refactoring) labels Nov 5, 2025
@joshtrichards joshtrichards marked this pull request as ready for review November 6, 2025 19:57
@joshtrichards joshtrichards requested a review from a team as a code owner November 6, 2025 19:57
@joshtrichards joshtrichards requested review from ArtificialOwl, CarlSchwan, icewind1991 and leftybournes and removed request for a team November 6, 2025 19:57
@joshtrichards joshtrichards added the 3. to review Waiting for reviews label Nov 6, 2025
Comment thread tests/lib/Security/SecureRandomTest.php Outdated
Comment on lines +93 to +95
/**
* @dataProvider invalidCharProviders
*/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @dataProvider invalidCharProviders
*/
#[DataProvider('invalidCharProviders')

(and add the class import)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread tests/lib/Security/SecureRandomTest.php Outdated

public function testDefaultCharsetBase64Characters(): void {
$randomString = $this->rng->generate(100);
$this->assertMatchesRegularExpression('/^[A-Za-z0-9\+\/]+$/', $randomString);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$this->assertMatchesRegularExpression('/^[A-Za-z0-9\+\/]+$/', $randomString);
$this->assertMatchesRegularExpression('/^[A-Za-z0-9\+\/]{100}$/', $randomString);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - apologies for the delay!

Comment thread tests/lib/Security/SecureRandomTest.php Outdated
Comment on lines +116 to +117
$this->assertMatchesRegularExpression('/^[abcd]+$/', $randomString);
$this->assertEquals(500, strlen($randomString));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$this->assertMatchesRegularExpression('/^[abcd]+$/', $randomString);
$this->assertEquals(500, strlen($randomString));
$this->assertMatchesRegularExpression('/^[abcd]{500}$/', $randomString);

Or keep the length check separate but then you can also do that upstairs.

Comment thread tests/lib/Security/SecureRandomTest.php Outdated
public function testUserProvidedValidCharset(): void {
$charset = '@#$!';
$randomString = $this->rng->generate(64, $charset);
$this->assertMatchesRegularExpression('/^[@#$!]+$/', $randomString);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$this->assertMatchesRegularExpression('/^[@#$!]+$/', $randomString);
$this->assertMatchesRegularExpression('/^[@#$!]{64}$/', $randomString);

* @throws \LengthException If $length <= 0.
* @throws \InvalidArgumentException if $characters contains non-ASCII characters, duplicates,
* or fewer than 4 unique characters.
* @since 8.0.0
Copy link
Copy Markdown
Member

@nickvergessen nickvergessen Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @since 8.0.0
* @since 35.0.0 $characters has to be at least 4 chars long, non-AS… etc
* @since 8.0.0

Add a @since for your change to the rules of the method

- Add constant for default $characters set
- Update docs for accuracy and clarity

Signed-off-by: Josh <josh.t.richards@gmail.com>
- Use a constant for the default $characters set (still same chars)
- Add more robust  reasonableness checks for $characters set
- Switch docblocks to point at interface docs to avoid duplication

Signed-off-by: Josh <josh.t.richards@gmail.com>
New test coverage for:

- charset validation failures (duplicates, too short, non-ASCII).
- default charset (CHAR_BASE64_RFC4648).
- randomness/unique output.
- minimum-sized valid charsets and large printable ASCII charset.
- a caller-provided, valid (non-predefined) custom charset.

And adjusts the length ranges tests:

- Added some smaller / more frequently used ranges.
- Added an "oddball" range to possibly catch weird stuff.
- Dropped excessive 64K test which does not need to run within our CI during standard PR runs (if deemed desirable maybe add it to an occasional stress tester, but I don't think it's necessary for now)

Signed-off-by: Josh <josh.t.richards@gmail.com>
Signed-off-by: Josh <josh.t.richards@gmail.com>
Signed-off-by: Josh <josh.t.richards@gmail.com>
Signed-off-by: Josh <josh.t.richards@gmail.com>
@joshtrichards joshtrichards force-pushed the jtr/refactor-SecureRandom branch from f2cdc9b to 971a1fe Compare June 1, 2026 16:48
Signed-off-by: Josh <josh.t.richards@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews enhancement ♻️ refactor Refactor code (not a bug fix, not a feature just refactoring) tests Related to tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants